import { EventEmitter } from "stream";
import { MyServer } from "./MyServer";
import { WebSocket } from "ws";
import { ApiMsgEnum, binaryDecode, binaryEncode, IModel, strdecode, strencode } from "../Common";
import { bufferToArrayBuffer } from "../Utils";

interface IItem {
    cb: Function;
    ctx: unknown;
}

export class Connection extends EventEmitter {

    private msgMap: Map<ApiMsgEnum, Array<IItem>> = new Map();
    constructor(private server: MyServer, private ws: WebSocket) {
        super();
        this.ws.on("close", () => {
            this.emit("close");
        })

        this.ws.on("message", (buffer: Buffer) => {
            try {
                const json =  binaryDecode(bufferToArrayBuffer(buffer));
                const { name, data } = json;
                if (this.server.apiMap.has(name)) {
                    try {
                        const cb = this.server.apiMap.get(name);
                        const res = cb(this, data);
                        this.sendMsg(name, {
                            success: true,
                            res,
                        })
                    } catch (e) {
                        this.sendMsg(name, {
                            success: false,
                            error: e.message,
                        })
                    }
                } else {
                    try {
                        if (this.msgMap.has(name)) {
                            this.msgMap.get(name).forEach(({ cb, ctx }) => {
                                cb.call(ctx, this, data);
                            });
                        }
                    } catch (e) {
                        console.error(e);
                    }
                }
            } catch (e) {
                console.error(e);
            }
        });
    }



    sendMsg<T extends keyof IModel["msg"]>(name: T, data: IModel["msg"][T]) {
        const msg = { name, data };
        const da = binaryEncode(name, data);
        const buffer = Buffer.from(da.buffer);
        this.ws.send(buffer);
    }

    listenMsg<T extends keyof IModel["msg"]>(name: T, cb: (connection: Connection, args: IModel["msg"][T]) => void, ctx: unknown) {
        if (this.msgMap.has(name)) {
            this.msgMap.get(name).push({ cb, ctx });
        } else {
            this.msgMap.set(name, [{ cb, ctx }]);
        }
    }

    unlistenMsg<T extends keyof IModel["msg"]>(name: T, cb: (connection: Connection, args: IModel["msg"][T]) => void, ctx: unknown) {
        if (this.msgMap.has(name)) {
            this.msgMap.set(name, this.msgMap.get(name).filter((item) => item.cb !== cb));
        }
    }

}